<?php
/**
 * Библиотека для создания thumbnails из картинок.
 *
 * <code>
 *   $thumb_engine = new Thumb();
 *   $source_image = $thumb_engine->imageFromFile('/tmp/source.jpg');
 *   $thumb_image = $thumb_engine->convert($source_image, array(
 *  	'filter' => 'scale',
 *  	'w'=> 100,
 *   ));
 *   $thumb_engine->imageToFile($thumb_image, '/tmp/thumb.jpg');
 * </code>
 */

class ThumbFileNotFoundError extends Exception {};
class ThumbInvalidImageError extends Exception {};

class Thumb
{
	const NS_PLUGINS = 'Thumb_Plugins';

	public function __construct ($cfg=array())
	{
		$this->cfg = $cfg;
		$this->code = isset($this->cfg['code']) ? $this->cfg['code'] : dirname(dirname(__FILE__)).'/';
	} // end of __construct
	public function imageFromFile ($file_source)
	{
		// 1. проверим файл ли это и существует ли он
		if (!$file_source) {
			throw new ThumbFileNotFoundError($file_source);
			return false;
		}

		// 2. создаем уменьшенные копии и тоже кладем их в хранилище
		// определим тип картинки потом создаем из файла ресурс и делаем ресамплинг, затем сохраняем в файл
		$gis = @getimagesize($file_source);
		if (empty($gis)) {
			// это не файл или не картинка
			// с этим работать не умеем
			throw new ThumbInvalidImageError($file_source);
			return false;
		}

		list($width, $height, $image_type, $img_string) = $gis;
		$mime_type = $gis['mime'];

		switch ( $image_type ) {
			case IMAGETYPE_GIF:
				$src_im = imagecreatefromgif($file_source);
			break;

			case IMAGETYPE_JPEG:
				$src_im = imagecreatefromjpeg($file_source);
			break;

			case IMAGETYPE_PNG:
				$src_im = imagecreatefrompng($file_source);
			break;

			default:
				throw new ThumbInvalidImageError($file_source);
				return false;
			break;
		}

		$img = new ThumbImage();
		$img->type = $image_type;
		$img->handle = $src_im;

		return $img;
	} // end of method imageFromFile
	public function imageToFile ($img, $file_dest)
	{
		// создаем файл
		// сохранить
		switch ($img->type) {
		case IMAGETYPE_GIF:
			imagegif($img->handle, $file_dest);
			break;

		case IMAGETYPE_JPEG:
			imagejpeg($img->handle, $file_dest);
			break;

		case IMAGETYPE_PNG:
			imagepng($img->handle, $file_dest);
			break;

		default:
			return false;
			break;
		}
		return true;
	} // end of method imageToFile

	public function convert ($src_im, $thumb, &$thumb_out=NULL)
	{
		$plugin = $this->usePlugin($thumb['filter']);

		if ($plugin) {
			$dst_im = $plugin->convert($src_im->handle, $thumb, $thumb_out);
			$dst = new ThumbImage();
			$dst->handle = $dst_im;
			$dst->type = isset($thumb_out['type']) ? $thumb_out['type'] : $src_im->type;
		} else {
			error(__METHOD__.'() filter "'.$name.'" not found ');
		}

		return $dst;
	} // end of function thumb

	public function usePlugin ($plugin_name)
	{
		if (!isset($this->plugins[$plugin_name])) {
			$plugin = NULL;
			if (is_object($plugin_name)) {
				$plugin = $plugin_name;
			} else {
				$plugin_class = self::NS_PLUGINS.'_'.$plugin_name;
				if (!class_exists($plugin_class)) {
					$source_php = $this->code.strtolower(strtr($plugin_class, '_', '/')).'.php';
					require_once $source_php;
				}
				if (class_exists($plugin_class)) {
					$plugin = new $plugin_class();
				}
			}
			if ($plugin) {
				$plugin->pluginInit($this);
			}
			$this->plugins[$plugin_name] = $plugin;
		}
		return $this->plugins[$plugin_name];
	} // end of method usePlugin
}

class ThumbImage
/**
 * Класс ThumbImage - картинка для Thumb.
 */
{
	var $handle=NULL;
	var $type = NULL;

	public function free ()
	{
		if (isset($this->handle)) {
			imagedestroy($this->handle);
			$this->handle = NULL;
		}
	} // end of method free
	public function getWidth ()
	{
		return imagesx($this->handle);
	} // end of method getWidth
	public function getHeight ()
	{
		return imagesy($this->handle);
	} // end of method getHeight
}

abstract class ThumbPlugin
{
	public function __construct ($cfg=array())
	{
		$this->cfg = $cfg;
	} // end of __construct
	public function pluginInit ($engine)
	{
		$this->engine = $engine;
	} // end of method init
	abstract public function convert ($src_im, $thumb, &$thumb_out=NULL);
}

// --- image library --- {{{
//
// FIXME : 2007-11-28 : lucky
// пока тут, потом вынесу в отдельный файл.
class ImageUtils
{
	static function copy_transparency ($dst_im, $src_im)
	/**
	 * Создать в $dst_im прозрачность, по возможности копируя цвет из $src_im.
	 *
	 * @param resource $dst_im Картинка назначения
	 * @param resource $src_im Исходная картинка
	 * @access public
	 * @return void
	 */
	{
		$trnprt_indx = imagecolortransparent($src_im);
		// If we have a specific transparent color
		if ($trnprt_indx >= 0) {
			// Get the original image's transparent color's RGB values
			$trnprt_color = imagecolorsforindex($src_im, $trnprt_indx);
			// Allocate the same color in the new image resource
			$trnprt_indx  = imagecolorallocatealpha($dst_im, $trnprt_color['red'], $trnprt_color['green'], $trnprt_color['blue'], $trnprt_color['alpha']);
		} else { // Make a transparent background color that don't have one allocated already
			// Create a new transparent color for image
			$trnprt_indx = imagecolorallocatealpha($dst_im, 0, 0, 0, 127);
		}

		// Turn off transparency blending (temporarily)
		$old_blendmode = imagealphablending($dst_im, false);
		// Completely fill the background of the new image with allocated color.
		imagefill($dst_im, 0, 0, $trnprt_indx);
		// Set the background color for new image to transparent
		imagecolortransparent($dst_im, $trnprt_indx);
		// Restore transparency blending
		imagealphablending($dst_im, $old_blendmode);
		imagesavealpha($dst_im, true);
	}

	static function dim_scale ($new_rect, $rect, $out=array())
	/**
	 * Вернуть размеры, пропорциональные указанным.
	 * 
	 * @param array $new_rect новый размер (w, h)
	 * @param array $rect исходный размер (w, h)
	 * @param array $out 
	 * @access public
	 * @return array $out
	 */
	{
		$new_rect = ImageUtils::dim_min_max($new_rect, $rect, $new_rect);
		$width      = $rect['w'];
		$height     = $rect['h'];
		$new_width  = $new_rect['w'];
		$new_height = $new_rect['h'];

		if (!$new_width && !$new_height) return false;

		if     ($new_width && $new_height) $factor = min($new_width / $width, $new_height / $height);
		elseif ($new_width)                $factor = $new_width / $width;
		elseif ($new_height)               $factor = $new_height / $height;

		$out['w'] = round ($width * $factor);
		$out['h'] = round ($height * $factor);

		return $out;
	} // end of static function image_scale
	static function dim_shrink ($new_rect, $rect, $out=array())
	/**
	 * Вернуть размеры, растянутые (сжатые) до указаных.
	 *
	 * Если размер не указан, принимается размер исходной картинки.
	 * 
	 * @param array $new_rect новый размер (w, h)
	 * @param array $rect исходный размер (w, h)
	 * @param array $out 
	 * @access public
	 * @return array $rect
	 */
	{
		$new_rect = ImageUtils::dim_min_max($new_rect, $rect, $new_rect);
		$width      = $rect['w'];
		$height     = $rect['h'];
		$new_width  = $new_rect['w'];
		$new_height = $new_rect['h'];

		$out['w'] = ($new_width  > 0) ? $new_width : $width;
		$out['h'] = ($new_height > 0) ? $new_height: $height;

		return $out;
	} // end of static function image_dim_shrink
	static function dim_min_max ($new_rect, $rect, $out=array())
	{
		if (!isset($new_rect['h'])) {
			if (isset($new_rect['max_h'])) {
				$out['h'] = ($rect['h'] > $new_rect['max_h']) ? $new_rect['max_h'] : $rect['h'];
			}
			if (isset($new_rect['min_h'])) {
				$out['h'] = ($rect['h'] < $new_rect['min_h']) ? $new_rect['min_h'] : $rect['h'];
			}
		}
		if (!isset($new_rect['w'])) {
			if (isset($new_rect['max_w'])) {
				$out['w'] = ($rect['w'] > $new_rect['max_w']) ? $new_rect['max_w'] : $rect['w'];
			}
			if (isset($new_rect['min_w'])) {
				$out['w'] = ($rect['w'] < $new_rect['min_w']) ? $new_rect['min_w'] : $rect['w'];
			}
		}
		return $out;
	} // end of static function image_dim_min_max
	//
}
// --- image library --- }}}

